package fast.loader;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import fast.data.Stat;
import fast.data.StatBlock;

public class ItemDB {
	
	private List<HashMap<Integer,Item>> db = new ArrayList<HashMap<Integer,Item>>();
	SocketBonusDB socketbonusDB = new SocketBonusDB();
	ItemSetDB itemSetDB = new ItemSetDB();
	GearBuffDB gearBuffDB = new GearBuffDB();
	ItemUpgrade itemUpgradeDB = new ItemUpgrade();
	
	ObjectDB<Item> objectDB = new ObjectDB<Item>("items");
	ObjectDB<Item> objectDBu1 = new ObjectDB<Item>("items_u1");
	ObjectDB<Item> objectDBu2 = new ObjectDB<Item>("items_u2");

	public ItemDB() {
		db.add(new HashMap<Integer,Item>());
		db.add(new HashMap<Integer,Item>());
		db.add(new HashMap<Integer,Item>());

		/*if(objectDB.exists() && objectDBu1.exists() && objectDBu2.exists()){
			db.get(0).putAll(objectDB.deserialize());
			db.get(1).putAll(objectDBu1.deserialize());
			db.get(2).putAll(objectDBu2.deserialize());
		}else{
			loadFromStorage();
			objectDB.serialize(db.get(0));
			objectDBu1.serialize(db.get(1));
			objectDBu2.serialize(db.get(2));
		}*/
		loadFromStorage();
	}
	
	public void loadFromStorage(){
		File file = new File(FastLoader.itemSource.getPath());
		if(!file.exists()){
			file.mkdir();
		}
		else if(file.exists()){
			System.out.println("Path "+FastLoader.itemSource.getPath()+" exists");
			File[] files = file.listFiles();
			for(int i = 0;i< files.length;i++){
				if(files[i].isFile()){
					int itemid = Integer.parseInt(files[i].getName().split("\\.")[0]);
					for(int lvl = 0; lvl < 3; lvl++){
						Item item = this.getItemFromLocal(itemid, lvl);
						if(item != null){
							if(!item.isUpgradable() && lvl > 0) continue;
							db.get(lvl).put(item.getId(), item);
						}
					}
				}
			}
		}
		System.out.println("directory battlenet imported");
	}
	
	public void put(int id,Item item){
		db.get(0).put(id, item);
	}

	public Item get(int id){
		return this.get(id, 0);
	}
	
	public Item get(int id,int upgradeLevel){
		if(!db.get(upgradeLevel).containsKey(id)){
			Item item = null;
			if(upgradeLevel > 0){
				item = this.getItemFromLocal(id, upgradeLevel);
			}else{
				//item = this.getItemFromWOWHEAD(id);
				item = this.getItemFromSource(id);
			}
			if(item != null)
				db.get(upgradeLevel).put(item.getId(), item);
		}
		System.out.println(db.get(upgradeLevel).get(id).getName());
		return db.get(upgradeLevel).get(id);
	}
	
	public ArrayList<Item> findAllWithInventorySlot(Slot slot){
		List<Integer> integers = slot.getInventorySlots();
		//Arrays.sort(integers);
		ArrayList<Item> list = new ArrayList<Item>();
		for(int k = 0; k < db.size(); k++){
			for(Integer i : db.get(k).keySet()){
				int invSlot = db.get(k).get(i).getInventorySlot();
				if(!integers.contains(invSlot)) continue;
				if(db.get(k).get(i).getMaxUpgradeLevel() < db.get(k).get(i).getUpgradeLevel()) continue;
				if(!db.get(k).get(i).isUpgradable() && db.get(k).get(i).getUpgradeLevel() > 0) continue;
				list.add(db.get(k).get(i));
			}
		}
		return list;
	}
	
	private JSONObject getItemDataBattleNetApi(int item_id){
		
		JSONObject stats = null;
		String json = "";
		
		File file = new File("./battlenet/"+item_id+".json");
		if(!file.exists()){

			json = FastLoader.loadURL("http://us.battle.net/api/wow/item/" + item_id);
			
			try {
				PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
				out.write(json);
				out.flush();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}else{
			BufferedReader in;
			try {
				in = (new BufferedReader(new FileReader(file)));
				json = in.readLine();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		try {
			stats = new JSONObject(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		
		return stats;
	}
	
	private Item getItemFromBATTLENET(int itemid){
		Item item = new Item();
		
		JSONObject data = getItemDataBattleNetApi(itemid);
		
		try {
			if(data.has("status")){
				if(data.getString("status").equals("nok"))
					return null;
			}
			//System.out.println(data.toString(1));
		} catch (JSONException e1) {
			e1.printStackTrace();
		}
		try {
			if(data.has("id"))
				item.setId(data.getInt("id"));
			item.setName(data.getString("name"));
			item.setIcon(data.getString("icon"));
			String nameDescription = data.getString("nameDescription");
			//item.setTooltip(tooltip);
			item.setQuality(data.getInt("quality"));
			item.setUpgradable(data.getBoolean("upgradable"));
			
			if(nameDescription.indexOf("Heroic Thunderforged") > -1){
				item.setTag("H-TF");
			}else if(nameDescription.indexOf("Heroic") > -1){
				item.setTag("H");
			}else if(nameDescription.indexOf("Thunderforged") > -1){
				item.setTag("TF");
			}else if(nameDescription.indexOf("Raid Finder") > -1){
				item.setTag("LFR");
			}else if(nameDescription.indexOf("Season") > -1){
				item.setTag("PVP");
			}

			if(data.has("bonusStats")){
				item.add(new StatBlock(data.getJSONArray("bonusStats")));
			}
			
			if(data.has("weaponInfo")){
				JSONObject weapon = data.getJSONObject("weaponInfo");
				item.setDps(weapon.getDouble("dps"));
				item.setSpeed(weapon.getDouble("weaponSpeed"));
				JSONObject damage = weapon.getJSONObject("damage");
				item.setMaxDmg(damage.getInt("max"));
				item.setMinDmg(damage.getInt("min"));
			}
			if(data.has("itemClass"))
				item.setItemClass(data.getInt("itemClass"));
			if(data.has("itemLevel"))
				item.setItemLevel(data.getInt("itemLevel"));
			if(data.has("itemSubClass")){
				item.setItemSubClass(data.getInt("itemSubClass"));
				if(data.has("weaponInfo")){
					item.setWeaponType();
				}
			}
			if(data.has("inventoryType")){
				item.setInventorySlot(data.getInt("inventoryType"));
			}
			
			// socket colors and socketbonus
			if(data.has("socketInfo")){
				JSONObject socketInfo = data.getJSONObject("socketInfo");
				//System.out.println(socketInfo.toString(1));
				JSONArray sockets = socketInfo.getJSONArray("sockets");
				int nsockets = sockets.length();
				item.setNumSockets(nsockets);
				for(int i = 0; i < nsockets; i++){
					JSONObject socket = sockets.getJSONObject(i);
					String color = socket.getString("type");
					// looks like magic hope it works
					item.gemSlot[i] = Socket.valueOf(color.toLowerCase());
				}
				if(socketInfo.has("socketBonus")){
					String socketBonus = socketInfo.getString("socketBonus");
					SocketBonus sb = null;
					for(Stat stat : Stat.possibleStats){
						String attr = stat.getSocketBonusName();
						String patternn = "\\+(\\d+) ("+attr+")";
						//System.out.println(patternn);
						Pattern pattern = Pattern.compile(patternn, Pattern.CASE_INSENSITIVE);
					    // In case you would like to ignore case sensitivity you could use this
					    // statement
					    // Pattern pattern = Pattern.compile("\\s+", Pattern.CASE_INSENSITIVE);
					    Matcher matcher = pattern.matcher(socketBonus);
					    // Check all occurrence
					    
					    while (matcher.find()) {
					    	if(sb == null){
					    		sb = new SocketBonus(stat, Integer.parseInt(matcher.group(1)));
					    	}else{
					    		sb.addStat(stat, Integer.parseInt(matcher.group(1)));
					    	}
					    }
					}
					item.setSocketBonus(sb);
				}
			}
			if(data.has("allowableClasses")){
				JSONArray allowableClasses = data.getJSONArray("allowableClasses");
				// TODO multiclass, like pvp items
				item.setReqClass(allowableClasses.getInt(0));
			}
			if(data.has("requiredSkill"))
				item.setReqSkill(data.getInt("requiredSkill"));
			// TODO requiredSkillRank
			// TODO itemSetID can be used but not necessary atm
			if(data.has("itemSet"))
				item.setSet(itemSetDB.getItemSetByItemId(itemid));
			String gb = gearBuffDB.getGearBuff(itemid);
			if(gb != null){
				item.setGearBuff(gb);
			}
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
			return null;
		}
		
		return item;
	}
	
	private JSONObject getItemDataWowHead(int item_id){
		
		JSONObject stats = null;
		String json = "";
		
		File file = new File("./wowhead/"+item_id+".xml");
		if(!file.exists()){
			json = FastLoader.loadURL("http://"+FastLoader.wowheadPrefix+".wowhead.com/item=" + item_id + "&xml");
		
			try {
				PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
				out.write(json);
				out.flush();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}else{
			BufferedReader in;
			try {
				in = (new BufferedReader(new FileReader(file)));
				json = in.readLine();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//System.out.println(json);
		try {
	        DocumentBuilderFactory dbf =
	            DocumentBuilderFactory.newInstance();
	        DocumentBuilder db = dbf.newDocumentBuilder();
	        InputSource is = new InputSource();
	        is.setCharacterStream(new StringReader(json));

	        Document doc = db.parse(is);
	        NodeList nodes = doc.getElementsByTagName("jsonEquip");
	        NodeList nodes2 = doc.getElementsByTagName("json");
	        
	        NodeList nodes3 = doc.getElementsByTagName("name");
	        NodeList nodes4 = doc.getElementsByTagName("icon");
	        
	        NodeList nodes5 = doc.getElementsByTagName("htmlTooltip");
	        
	        NodeList nodes6 = doc.getElementsByTagName("quality");

	        Element element = (Element) nodes.item(0);
	        if(element != null){
		        stats = new JSONObject("{" + element.getTextContent()+ "}");
		        Element element2 = (Element) nodes2.item(0);
		        JSONObject stats2 = new JSONObject("{" + element2.getTextContent()+ "}");
		        stats.put("extra", stats2);
	           
		        Element element3 = (Element) nodes3.item(0);
		        String stats3 = element3.getTextContent();
		        stats.put("name", stats3);
		        
		        Element element4 = (Element) nodes4.item(0);
		        String stats4 = element4.getTextContent();
		        stats.put("icon", stats4);
		        
		        Element element5 = (Element) nodes5.item(0);
		        String stats5 = element5.getTextContent();
		        stats.put("tooltip", stats5);
		        
		        Element element6 = (Element) nodes6.item(0);
		        String stats6 = element6.getAttribute("id");
		        stats.put("quality", stats6);
	        }
	    }
	    catch (Exception e) {
	    	System.out.println(item_id);
	        e.printStackTrace();
	    }
	    return stats;
	}
	
	private Item getItemFromWOWHEAD(int itemid){
		Item item = new Item();
		
		JSONObject data = getItemDataWowHead(itemid);
		JSONObject extra;
		try {
			extra = data.getJSONObject("extra");
			
			if(extra.has("id"))
				item.setId(extra.getInt("id"));
			item.setName(data.getString("name"));
			item.setIcon(data.getString("icon"));
			String tooltip = data.getString("tooltip");
			item.setTooltip(tooltip);
			item.setQuality(data.getInt("quality"));
			item.setUpgradable(true);
			
			if(tooltip.indexOf("Heroic Thunderforged") > -1){
				item.setTag("H-TF");
			}else if(tooltip.indexOf("Heroic Warforged") > -1){
					item.setTag("H-WF");
			}else if(tooltip.indexOf("Heroic") > -1){
				item.setTag("H");
			}else if(tooltip.indexOf("Thunderforged") > -1){
					item.setTag("TF");
			}else if(tooltip.indexOf("Warforged") > -1){
				item.setTag("WF");
			}else if(tooltip.indexOf("Flexible") > -1){
				item.setTag("FLEX");
			}else if(tooltip.indexOf("Raid Finder") > -1){
				item.setTag("LFR");
			}

			item.add(new StatBlock(data));
			
			/*if(data.has("str"))
				item.setStr(data.getInt("str"));
			if(data.has("agi"))
				item.setAgi(data.getInt("agi"));
			if(data.has("sta"))
				item.setSta(data.getInt("sta"));
			if(data.has("int"))
				item.setInt(data.getInt("int"));
			if(data.has("spi"))
				item.setSpi(data.getInt("spi"));
			if(data.has("hitrtng"))
				item.setHit(data.getInt("hitrtng"));
			if(data.has("critstrkrtng"))
				item.setCrit(data.getInt("critstrkrtng"));
			if(data.has("hastertng"))
				item.setHaste(data.getInt("hastertng"));
			if(data.has("exprtng"))
				item.setExp(data.getInt("exprtng"));
			if(data.has("mastrtng"))
				item.setMastery(data.getInt("mastrtng"));
			if(data.has("splpwr"))
				item.setSp(data.getInt("splpwr"));*/
			
			if(data.has("dps"))
				item.setDps(data.getDouble("dps"));
			if(data.has("mledmgmax"))
				item.setMaxDmg(data.getInt("mledmgmax"));
			if(data.has("mledmgmin"))
				item.setMinDmg(data.getInt("mledmgmin"));
			if(data.has("speed"))
				item.setSpeed(data.getDouble("speed"));
			if(extra.has("classs"))
				item.setItemClass(extra.getInt("classs"));
			if(extra.has("level"))
				item.setItemLevel(extra.getInt("level"));
			if(extra.has("subclass")){
				item.setItemSubClass(extra.getInt("subclass"));
				if(data.has("dps")){
					item.setWeaponType();
				}
			}
			if(extra.has("slot")){
				//item.setInventoryType(extra.getInt("slot"));
				item.setInventorySlot(extra.getInt("slot"));
			}
			
			// socket colors and socketbonus
			if(data.has("nsockets"))
				item.setNumSockets(data.getInt("nsockets"));
			if(data.has("socket1"))
				item.gemSlot[0] = Gem.SocketColors[data.getInt("socket1")];
			if(data.has("socket2"))
				item.gemSlot[1] = Gem.SocketColors[data.getInt("socket2")];
			if(data.has("socket3"))
				item.gemSlot[2] = Gem.SocketColors[data.getInt("socket3")];
			if(data.has("socketbonus")){
				item.setSocketBonus(socketbonusDB.getSocketBonus(data.getInt("socketbonus")));
				if(item.getSocketBonus() == null){
					System.out.println(data.getString("name") + " " + data.getInt("socketbonus"));
				}
			}
			if(extra.has("reqclass"))
				item.setReqClass(extra.getInt("reqclass"));
			if(data.has("reqskill"))
				item.setReqSkill(data.getInt("reqskill"));
			if(data.has("itemset"))
				item.setSet(itemSetDB.getItemSetByItemId(itemid));
			String gb = gearBuffDB.getGearBuff(itemid);
			if(gb != null){
				item.setGearBuff(gb);
			}
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
			return null;
		}
		
		return item;
	}
	
	public Item getItemFromLocal(int id, int upgradeLevel) {
		Item item = getItemFromSource(id);
		if(upgradeLevel == 0 || item == null || !item.isUpgradable()) 
			return item;
		int lvl_by = item.getQuality() == 3 ? 8 : 4;
		item.setItemLevel(item.getItemLevel()+lvl_by*upgradeLevel);
		item.setUpgradeLevel(upgradeLevel);
		item.setName("[U"+upgradeLevel+"] "+item.getName());
		itemUpgradeDB.populate_item_upgrade_level_2(item);
		//System.out.println(item);
		return item;
	}
	
	public Item getItemFromSource(int itemid){
		switch(FastLoader.itemSource){
		case wowapi:
			return this.getItemFromBATTLENET(itemid);
		case wowhead:
			return this.getItemFromWOWHEAD(itemid);
		default:
			return null;
		}
	}
}
